using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

using System.Data.SQLite;

namespace GraphFS
{
	public class SQLiteAccess : IAccess
	{


		private List<Int64> nodesIndex2 = new List<Int64>();
		private Dictionary<Int64, List<Int64>> nodesNeighborsIndex2 = new Dictionary<Int64, List<Int64>>();

		private string mapName;
		private FSNodesCache nodesCache;

		private SQLiteConnection sqlConnection;



		public SQLiteAccess(string mapName)
		{
			this.mapName = mapName;
			Initialize();
		}




		private void Initialize()
		{
			nodesCache = new FSNodesCache();
			sqlConnection = new SQLiteConnection(@"Data Source=G:\gps\gps.sqlite;Pooling=true;FailIfMissing=false");
			/*sqlCommand = new SQLiteCommand(sqlConnection);
			sqlCommand.CommandType = System.Data.CommandType.Text;*/
			sqlConnection.Open();

			LoadNodesIndex();
			//InitializeStreams(mapName);
			//getBounds();
		}


		public GeoRectangle GetBounds()
		{
			GeoRectangle ret = GeoRectangle.Empty;
			SQLiteCommand sqlCommand = new SQLiteCommand(sqlConnection);
			sqlCommand.CommandText = "SELECT min(lat), max(lat), min(lon), max(lon) FROM nodes";
			SQLiteDataReader sdr = sqlCommand.ExecuteReader();
			while (sdr.Read())
			{
				ret = new GeoRectangle(new GeoPoint(sdr.GetDouble(0), sdr.GetDouble(2)), new GeoPoint(sdr.GetDouble(1), sdr.GetDouble(3)));
			}
			sdr.Close();
			sdr.Dispose();
			Console.WriteLine(ret);
			return ret;
		}

		private void LoadNodesIndex()
		{
			SQLiteCommand sqlCommand = new SQLiteCommand(sqlConnection);
			sqlCommand.CommandText = "SELECT nodeId FROM nodes";
			SQLiteDataReader sdr = sqlCommand.ExecuteReader();
			while (sdr.Read())
			{
				nodesIndex2.Add(sdr.GetInt64(0));
			}
			sdr.Close();
			sdr.Dispose();

			sqlCommand = new SQLiteCommand(sqlConnection);
			sqlCommand.CommandText = "SELECT nodeId,neighborId FROM neighbors";
			sdr = sqlCommand.ExecuteReader();
			while (sdr.Read())
			{
				Int64 nodeId = sdr.GetInt64(0);
				if (!nodesNeighborsIndex2.ContainsKey(nodeId))
				{
					nodesNeighborsIndex2.Add(nodeId, new List<Int64>());
				}
				nodesNeighborsIndex2 [nodeId].Add(sdr.GetInt64(1));
			}
			sdr.Close();
			sdr.Dispose();
		}

		public FSNode GetNode(Int64 nodeId)
		{
			if (nodesCache.ContainsKey(nodeId))
			{
				return nodesCache [nodeId];
			}
			else
			{
				SQLiteCommand sqlCommand = new SQLiteCommand(sqlConnection); 
				sqlCommand.CommandText = "SELECT lat,lon FROM nodes WHERE nodeId=" + nodeId.ToString();
				SQLiteDataReader sdr = sqlCommand.ExecuteReader();
				while (sdr.Read())
				{
					FSNode ret = new FSNode(nodeId, sdr.GetDouble(0), sdr.GetDouble(0));
					nodesCache.Add(nodeId, ret);
					return ret;

				}
				sdr.Close();
				sdr.Dispose();
			}
			return null;
		}

		public List<FSNode> GetNeighbors(Int64 nodeId)
		{

			List<FSNode> ret = new List<FSNode>();
			SQLiteCommand sqlCommand = new SQLiteCommand(sqlConnection);
			sqlCommand.CommandText = "SELECT neighborId FROM neighbors WHERE nodeId=" + nodeId.ToString();
			SQLiteDataReader sdr = sqlCommand.ExecuteReader();
			while (sdr.Read())
			{
				ret.Add(GetNode(sdr.GetInt64(0)));
			}
			sdr.Close();
			sdr.Dispose();
			return ret;
		}

		public List<FSNode> GetNodes(GeoRectangle rect)
		{
			return GetNodes(rect, 1);
		}

		public List<FSNode> GetNodes(GeoRectangle rect, int lod)
		{

			List<FSNode> ret = new List<FSNode>();
			FSNode n = null;

			SQLiteCommand sqlCommand = new SQLiteCommand(sqlConnection);
			sqlCommand.CommandText = String.Format("SELECT nodeId FROM nodes WHERE lat>={0} AND lat<={1} AND lon>={2} AND lon<={3} and lod={4}", rect.Location.Lat, rect.Location.Lon, rect.Location.Lat + rect.Size.Width, rect.Location.Lon + rect.Size.Height, lod);
			SQLiteDataReader sdr = sqlCommand.ExecuteReader();
			while (sdr.Read())
			{

				Int64 cNodeId = sdr.GetInt64(0);
				if (nodesCache.ContainsKey(cNodeId))
				{
					ret.Add(nodesCache [cNodeId]);
				}
				else
				{
					ret.Add(GetNode(cNodeId));
				}

				
			}
			sdr.Close();
			sdr.Dispose();

			return ret;
		}




		#region IDisposable Members

		public void Dispose()
		{
			((IDisposable) this).Dispose();
		}

		#endregion

		#region IDisposable Members

		void IDisposable.Dispose()
		{
		}

		#endregion
	}




}
